package edu.xidian.onlinedocument.service;

import edu.xidian.onlinedocument.entity.*;
import edu.xidian.onlinedocument.handler.excpetions.custom.CustomException;
import edu.xidian.onlinedocument.repository.DocumentJPARepository;
import edu.xidian.onlinedocument.views.ResultCode;
import edu.xidian.onlinedocument.repository.UserDataJPARepository;
import edu.xidian.onlinedocument.repository.UserTokenJPARepository;

import edu.xidian.onlinedocument.repository.specification.UserSpecification;
import edu.xidian.onlinedocument.utils.SHA256Util;
import edu.xidian.onlinedocument.utils.TokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * User相关业务
 * 主要涉及登录相关以及身份验证相关的业务，包括登录、注册、注销、身份验证和token的管理等
 */
@Service
public class UserService {
    private final UserDataJPARepository userDataJPARepository;

    private final UserTokenJPARepository userTokenJPARepository;

    private final UserSpecification userSpecification;

    private final DocumentJPARepository documentJPARepository;

    private final CustomDraw customDraw;

    // 构造方法加@Autowired注释，标识bean注入
    @Autowired
    public UserService(UserDataJPARepository userDataJPARepository,
                       UserTokenJPARepository userTokenJPARepository,
                       UserSpecification userSpecification,
                       DocumentJPARepository documentJPARepository,
                       CustomDraw customDraw){
        this.userDataJPARepository = userDataJPARepository;
        this.userTokenJPARepository = userTokenJPARepository;
        this.userSpecification = userSpecification;
        this.documentJPARepository = documentJPARepository;
        this.customDraw = customDraw;
    }

    /**
     * 注册功能
     * @param userName 用户名
     * @param userNum 用户账号
     * @param key 密码
     */
    public void register(String userName, String userNum, String key){
        String sha256Key = SHA256Util.getSHA256(key);
        addUser(userName, userNum, sha256Key);
    }

    /**
     * 登录功能，如果用户信息验证成功则返回一个token，如果已经登陆了（有token）则生成一个新的token并返回
     * @param num 账号
     * @param key 密码
     * @return token
     */
    public String login(String num, String key){
        // 将密码转为sha256加密的密钥
        String sha256Key = SHA256Util.getSHA256(key);
        // 验证用户信息
        UserData userData = checkAccountAndPassword(num, sha256Key);
        String token = TokenUtil.createToken(num);
        if(isExistToken(userData.getUserId())){
            deleteToken(userData.getUserId());
        }
        addToken(userData.getUserId(), token);
        return token;
    }

    /**
     * 注销功能
     * @param num 账号
     */
    public void logout(String num){
        userTokenJPARepository.deleteUserTokenByUserData_UserNum(num);
        documentJPARepository.deleteDocumentDataByCreatorData_UserNum(num);
        userDataJPARepository.deleteUserDataByUserNum(num);
    }

    /**
     * 获取全部用户信息
     * @param userId 用户id
     * @return 用户信息
     */
    public UserData getUserDataByUserId(int userId) {
        if(!userDataJPARepository.existsUserDataByUserId(userId)) {
            throw new CustomException(ResultCode.USER_ID_NOT_EXIST);
        }
        return userDataJPARepository.findUserDataByUserId(userId);
    }

    /**
     * 获取全部用户信息
     * @param userNum 用户账号
     * @return 用户信息
     */
    public UserData getUserDataByUserNum(String userNum) {
        if(!userDataJPARepository.existsUserDataByUserNum(userNum)) {
            throw new CustomException(ResultCode.USER_ACCOUNT_NOT_EXIST);
        }
        return userDataJPARepository.findUserDataByUserNum(userNum);
    }

    /**
     * 获取全部用户信息
     * @param token token
     * @return 用户信息
     */
    public UserData getUserDataByToken(String token){
        if(!userDataJPARepository.existsUserDataByUserToken_UserToken(token)) {
            throw new CustomException(ResultCode.TOKEN_CHECK_FAILED);
        }
        return userDataJPARepository.findUserDataByUserToken_UserToken(token);
    }

    /**
     * 获取部分用户信息：用户名
     * @param userNum 用户账号
     * @return 用户信息
     */
    public UserData getOneUserNameByUserNum(String userNum){
        UserData userData = getUserDataByUserNum(userNum);
        userData.setUserNum(null);
        return customDraw.takeUserNumAndName(userData);
    }

    /**
     * 获取部分用户信息：用户账号和用户名
     * @param userNum 用户账号
     * @return 用户信息
     */
    public UserData getOneUserInfoByUserNum(String userNum){
        UserData userData = getUserDataByUserNum(userNum);
        return customDraw.takeUserNumAndName(userData);
    }

    /**
     * 获取部分用户信息：用户账号和用户名，以及所创建的文档
     * @param userNum 用户账号
     * @return 用户信息
     */
    public UserData getOneUserInfoAndDocumentList(String userNum){
        // return userSpecification.findOneUserDataWithDocumentByUserNum(userNum);
        UserData userData = userDataJPARepository.findUserDataByUserNum(userNum);
        return customDraw.takeUserNumAndNameAndDocuments(userData);
    }

    /**
     * 获取部分用户信息：用户账号和用户名，以及所编辑的文档
     * @param userNum 用户账号
     * @return 用户信息
     */
    public UserData getOneUserInfoAndEditorList(String userNum){
        // return userSpecification.findOneUserDataWithDocumentByUserNum(userNum);
        UserData userData = userDataJPARepository.findUserDataByUserNum(userNum);
        return customDraw.takeUserNumAndNameAndEditors(userData);
    }

    /**
     * 获取部分用户信息：用户账号和用户名，以及所创建和编辑的文档
     * @param userNum 用户账号
     * @return 用户信息
     */
    public UserData getOneUserInfoAndDocumentListAndEditorList(String userNum){
        // return userSpecification.findOneUserDataWithDocumentByUserNum(userNum);
        UserData userData = userDataJPARepository.findUserDataByUserNum(userNum);
        return customDraw.takeUserNumAndNameAndDocumentsAndEditors(userData);
    }

    /**
     * 获取所有的用户信息（账号、用户名和密码）
     * @return 用户信息列表
     */
    public List<UserData> getAllUsersData(){
        return userDataJPARepository.findAll();
    }

    /**
     * 获取所有的用户信息（账号和用户名），以及所创建的文档
     * @return 用户信息列表
     */
    public List<UserData> getAllUserInfoAndDocumentList(){
        return customDraw.takeUserNumAndNameAndDocuments(userDataJPARepository.findAll());
    }

    /**
     * 获取所有的用户信息（账号和用户名），以及所编辑的文档
     * @return 用户信息列表
     */
    public List<UserData> getAllUserInfoAndEditorList(){
        return customDraw.takeUserNumAndNameAndEditors(userDataJPARepository.findAll());
    }

    /**
     * 获取所有的用户信息（账号和用户名），以及所创建和编辑的文档
     * @return 用户信息列表
     */
    public List<UserData> getAllUserInfoAndDocumentListAndEditorList(){
        return customDraw.takeUserNumAndNameAndDocumentsAndEditors(userDataJPARepository.findAll());
    }

    /********************************************************
     * 以下属于模块功能，controller一般不直接调用，
     * service的集成方法调用，规定了一系列的简单sql操作
     * 增删改的操作只返回boolean类型值，因为只需要知道sql语句执行是否成功即可，不关心报错的具体类型
     * 查找操作
     ********************************************************/

    /**
     * 根据用户userNum获取其userId
     * @param userNum 用户账号
     * @return 用户id
     */
    private Integer getUserIdByUserNum(String userNum) {
        customDraw.checkUserNum(userNum);
        return userDataJPARepository.findUserIdByUserNum(userNum);
    }

    /**
     * 根据用户userToken获取其userId
     * @param userToken token
     * @return 用户id
     */
    public Integer getUserIdByUserToken(String userToken) {
        customDraw.checkUserToken(userToken);
        return userDataJPARepository.findUserIdByUserToken(userToken);
    }

    /**
     * 查询是否存在该用户
     * @param userNum 用户账号
     * @return 存在该用户（是 true / 否 false）
     */
    public boolean isExistUser(String userNum){
        return userDataJPARepository.existsUserDataByUserNum(userNum);
    }

    /**
     * 查询是否存在该用户
     * @param userId 用户id
     * @return 存在该用户（是 true / 否 false）
     */
    public boolean isExistUser(int userId){
        return userDataJPARepository.existsUserDataByUserId(userId);
    }

    /**
     * 查询某用户是否有token
     * @param userId 用户id
     * @return 存在该token（是 true / 否 false）
     */
    public boolean isExistToken(int userId){
        return userTokenJPARepository.existsById(userId);
    }

    /**
     * 查询是否存在该token
     * @param token token
     * @return 存在该token（是 true / 否 false）
     */
    public boolean isExistToken(String token){
        return userTokenJPARepository.existsUserTokenByUserToken(token);
    }

    /**
     * 验证账号和密码
     * @param num 账号
     * @param key 密码
     * @return 对应的用户信息，如果不存在，返回null
     */
    private UserData checkAccountAndPassword(String num, String key){
        UserData user = userDataJPARepository.findUserDataByUserNumAndUserKey(num, key);
        if(user == null){
            throw new CustomException(ResultCode.USER_ACCOUNT_OR_PASSWORD_ERROR);
        }
        return user;
    }

    /**
     * 验证账户和token是否一一对应
     * @param userNum 用户账号
     * @param userToken token
     * @return 是否对应
     */
    private boolean checkUserAndToken(String userNum, String userToken){
        UserData user = userDataJPARepository.findUserDataByUserNum(userNum);
        UserTokenData token = userTokenJPARepository.findUserTokenByUserToken(userToken);
        if(user == null){
            throw new CustomException(ResultCode.USER_ACCOUNT_NOT_EXIST);
        }
        if(token == null){
            throw new CustomException(ResultCode.TOKEN_CHECK_FAILED);
        }
        if(user.getUserId() == token.getUserId()){
            throw new CustomException(ResultCode.VERIFY_FAILED);
        }
        return true;
    }

    /**
     * 增加一名用户
     * @param userName 用户名
     * @param userNum 用户账号
     * @param key 密码
     */
    public void addUser(String userName, String userNum, String key){
        // 如果账号已存在，，则报错
        if(isExistUser(userNum)){
            throw new CustomException(ResultCode.USER_ACCOUNT_IS_EXIST);
        }
        userDataJPARepository.addUserData(userName, userNum, key);
    }

    /**
     * 删除一名用户
     * @param userNum 用户账号
     */
    private void deleteUser(String userNum){
        // 如果账号不存在，则报错
        if(!isExistUser(userNum)){
            throw new CustomException(ResultCode.USER_ACCOUNT_NOT_EXIST);
        }
        userDataJPARepository.deleteUserDataByUserNum(userNum);
    }

    /**
     * 添加对应用户的token
     * @param userId 用户id
     * @param token token
     */
    private void addToken(int userId, String token){
        if(isExistToken(userId)){
            throw new CustomException(ResultCode.TOKEN_IS_EXIST);
        }
        userTokenJPARepository.saveUserToken(userId, token, TokenUtil.getCreateTime(token));
    }

    /**
     * 删除对应用户的token
     * @param userId 用户id
     */
    private void deleteToken(int userId){
        customDraw.checkUserId(userId);
        // 不存在该用户，则报错
        if(!isExistUser(userId)) {
            throw new CustomException(ResultCode.USER_ID_NOT_EXIST);
        }
        userTokenJPARepository.deleteUserTokenByUserId(userId);
    }

    /**
     * 删除对应用户的token
     * @param userNum 用户账号
     */
    private void deleteToken(String userNum){
        // 不存在该用户，则报错
        if(!isExistUser(userNum)) {
            throw new CustomException(ResultCode.USER_ID_NOT_EXIST);
        }
        userTokenJPARepository.deleteUserTokenByUserData_UserNum(userNum);
    }
}
